#ifndef FPAL_HPP
#define FPAL_HPP

#include "ap_fixed.h"
#include "ap_int.h"

// -----------------------------------------------------------
// 封装在命名空间 fpal 中
// -----------------------------------------------------------
namespace fpal {

// --- 1. 定点数数据类型定义 ---

// fp_color_channel_t (U.8.0 - 8位无符号整数)
// 用于精确表示每个颜色通道（R, G, B）的强度值，直接对应标准的 24 位 RGB888 颜色格式。
// 位宽 `8` 位提供 `0` 到 `255` 的 `256` 级强度。
typedef ap_uint<8> fp_color_channel_t;

// fp_z_buffer_t (U.16.0 - 16位无符号整数)
// 专用于深度缓冲区的存储与比较。此格式代表经过归一化和缩放的逆深度值 (1/Z)，
// `U.16.0`（纯整数）足以表示 `0` 到 `65535` 的深度范围。
typedef ap_uint<16> fp_z_buffer_t;

// fp_coord_t (S.24.10 - 34位)
// 用于精确表示屏幕空间像素坐标、三角形边缘方程系数（A, B, C），以及深度梯度（dzdx, dzdy）。
// W=34 (总位宽), I=24 (整数位宽, 含符号位), F=10 (小数位宽)
// AP_RND_CONV: 四舍五入到最近偶数, AP_SAT: 饱和
typedef ap_fixed<34, 24, AP_RND_CONV, AP_SAT> fp_coord_t; // 保持AP_RND_CONV，在需要严格四舍五入到整数时手动 +0.5

// fp_screen_pixel_idx_t (U.11.0 - 11位无符号整数)
// 用于表示屏幕 X/Y 坐标的纯整数部分。位宽 `11` 位可表示 `0` 到 `2047`。
typedef ap_uint<11> fp_screen_pixel_idx_t;


// -----------------------------------------------------------
// 通用辅助函数
// -----------------------------------------------------------
// 适用于所有 numeric 类型 (ap_fixed, ap_int, int, float, double等)
template<typename T>
inline T clamp(T val, T min_val, T max_val) {
    #pragma HLS INLINE
    return (val < min_val) ? min_val : ((val > max_val) ? max_val : val);
}

template<typename T_IN, int W, int I, ap_q_mode Q, ap_o_mode O>
inline ap_fixed<W, I, Q, O> round_half_up_fx(T_IN val) {
    #pragma HLS INLINE
    return val;
}


// -----------------------------------------------------------
// 2. 核心算术运算集合 (HLS 综合为 RTL 模块)
//    - 根据需求，只提供 fp_coord_t 类型的运算，因为其他 ap_uint 类型可直接使用 C++ 运算符
//    - 所有函数添加 _fx 后缀，明确是定点数运算
// -----------------------------------------------------------

// add_fx (定点数加法)
inline fp_coord_t add_fx(fp_coord_t a, fp_coord_t b) {
    #pragma HLS INLINE
    return a + b;
}

// sub_fx (定点数减法)
inline fp_coord_t sub_fx(fp_coord_t a, fp_coord_t b) {
    #pragma HLS INLINE
    return a - b;
}

// mul_fx (定点数乘法)
inline fp_coord_t mul_fx(fp_coord_t a, fp_coord_t b) {
    #pragma HLS INLINE
    // HLS会自动识别并优化利用 FPGA 的 DSP Slice。
    return a * b;
}

// cmp_gt_fx (定点数大于比较)
template<typename T>
inline bool cmp_gt_fx(T a, T b) {
    #pragma HLS INLINE
    return a > b;
}

// clamp_u8_fx (定点数到 U.8.0 的饱和裁剪)
// 用于将颜色通道值裁剪到 `0-255` 的有效范围。
// 输入可以是 fp_coord_t 或其他数值类型，但转换到 fp_color_channel_t (U.8.0)。
template<typename T_IN>
inline fp_color_channel_t clamp_u8_fx(T_IN val) {
    #pragma HLS INLINE

    fp_coord_t temp_coord_val = (fp_coord_t)val; // 将输入转换为统一的 fp_coord_t 类型
    
    // 显式钳制到 [0.0, 255.0]
    fp_coord_t zero_fx = (fp_coord_t)0.0;
    fp_coord_t max_color_val_fx = (fp_coord_t)255.0;
    temp_coord_val = clamp(temp_coord_val, zero_fx, max_color_val_fx);

    fp_coord_t half_fx = (fp_coord_t)0.5;

    // 为了实现 round half up (2.5 -> 3, -2.5 -> -2)
    // 针对非负数 (temp_coord_val >= 0)
    // val + 0.5, 再截断。
    return (fp_color_channel_t)(temp_coord_val + half_fx);
}


// -----------------------------------------------------------
// 3. 定点数类型转换函数
//    确保数据在不同精度和范围的定点数类型之间正确传递和转换，
//    同时进行必要的饱和或截断处理，保证数据一致性。
// -----------------------------------------------------------

// to_fp_coord
// fp_coord_t 本身定义了 AP_SAT 饱和模式和 AP_RND_CONV 舍入模式。
template<typename T_IN>
inline fp_coord_t to_fp_coord(T_IN val) {
    #pragma HLS INLINE
    return (fp_coord_t)val; // fp_coord_t 带有 AP_SAT 和 AP_RND_CONV 模式，会自动饱和和舍入。
}

// to_fp_z_buffer
// 将通用类型转换为 fp_z_buffer_t (U.16.0)。
// 转换过程中需要钳制到 [0, 65535] 并四舍五入到最近的整数。
template<typename T_IN>
inline fp_z_buffer_t to_fp_z_buffer(T_IN val) {
    #pragma HLS INLINE
    // PS 侧负责将浮点逆深度值精确映射到此 16 位无符号整数范围，确保不发生精度损失或溢出。
    // PL 侧接收到的 fp_coord_t (S.24.10) 可能需要进行转换。
    // 假定 val 可能带有小数，需要四舍五入。
    // 钳制到 [0, 65535] 范围，然后四舍五入并转换为 ap_uint<16>。

    fp_coord_t temp_coord_val = (fp_coord_t)val; // 转换为统一的 fp_coord_t 类型

    // 显式钳制到 [0.0, 65535.0]
    fp_coord_t zero_fx = (fp_coord_t)0.0;
    fp_coord_t max_z_val_fx = (fp_coord_t)65535.0;
    temp_coord_val = clamp(temp_coord_val, zero_fx, max_z_val_fx);

    // 四舍五入到最近的整数。同 clamp_u8_fx 的逻辑。
    fp_coord_t half_fx = (fp_coord_t)0.5;
    return (fp_z_buffer_t)(temp_coord_val + half_fx);
}

// to_fp_color_channel
// 将通用类型转换为 fp_color_channel_t (U.8.0)。
// 直接调用 clamp_u8_fx 来实现，保持接口名。
template<typename T_IN>
inline fp_color_channel_t to_fp_color_channel(T_IN val) {
    #pragma HLS INLINE
    return clamp_u8_fx(val); // 调用已处理四舍五入和钳制的 clamp_u8_fx
}

// to_fp_screen_pixel_idx
// 将通用类型转换为 fp_screen_pixel_idx_t (U.11.0)。
// 转换过程中需要钳制到 [0, 2047] 并四舍五入到最近的整数。
template<typename T_IN>
inline fp_screen_pixel_idx_t to_fp_screen_pixel_idx(T_IN val) {
    #pragma HLS INLINE
    // 钳制到 [0, 2047] 范围，然后四舍五入并转换为 ap_uint<11>。

    fp_coord_t temp_coord_val = (fp_coord_t)val; // 转换为统一的 fp_coord_t 类型

    // 显式钳制到 [0.0, 2047.0]
    fp_coord_t zero_fx = (fp_coord_t)0.0;
    fp_coord_t max_idx_val_fx = (fp_coord_t)2047.0;
    temp_coord_val = clamp(temp_coord_val, zero_fx, max_idx_val_fx);

    // 四舍五入到最近的整数。同 clamp_u8_fx 的逻辑。
    fp_coord_t half_fx = (fp_coord_t)0.5;
    return (fp_screen_pixel_idx_t)(temp_coord_val + half_fx);
}


} // namespace fpal

#endif // FPAL_HPP
